package com.zlx.mall.basic.util.json;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.ser.std.DateSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * @author qingke
 * 序列化别名
 * @see com.fasterxml.jackson.annotation.JsonProperty
 * 反序列化别名
 * @see com.fasterxml.jackson.annotation.JsonAlias
 * 忽略序列化
 * @see com.fasterxml.jackson.annotation.JsonIgnore
 **/
public final class JacksonUtil {

    private static final ObjectMapper MAPPER = new ObjectMapper();
    private static final MapTypeReference MAP_TYPE = new MapTypeReference();
    private static final StringMapTypeReference STRING_MAP_TYPE = new StringMapTypeReference();
    private static final SortedMapTypeReference SORTED_MAP_TYPE = new SortedMapTypeReference();
    private static final StringSortedMapTypeReference STRING_SORTED_MAP_TYPE = new StringSortedMapTypeReference();
    private static final ListTypeReference LIST_TYPE = new ListTypeReference();
    private static final StringListTypeReference STRING_LIST_TYPE = new StringListTypeReference();
    private static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    private static final String DATE_FORMAT = "yyyy-MM-dd";
    private static final String TIME_FORMAT = "HH:mm:ss";

    static {
        // 日期序列化设置
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATE_TIME_FORMAT);
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
        DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern(TIME_FORMAT);

        // java.util.Date
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DATE_TIME_FORMAT);
        MAPPER.setDateFormat(simpleDateFormat);

        // java.time.LocalDateTime
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(Date.class, new DateSerializer(false, simpleDateFormat));
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter));
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(dateFormatter));
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(timeFormatter));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(dateFormatter));
        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(timeFormatter));
        MAPPER.registerModule(javaTimeModule);

        // 关闭序列化日期为时间戳配置
        MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        // 关闭无访问器异常抛出
        MAPPER.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        // 关闭反序列化未知属性解析异常抛出
        MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
    }

    public static ObjectMapper getMapper() {
        return MAPPER;
    }

    public static JavaType constructParametricType(Class<?> parametrized, Class<?>... parameterClasses) {
        return MAPPER.getTypeFactory().constructParametricType(parametrized, parameterClasses);
    }

    public static ObjectNode newObjectNode() {
        return new ObjectNode(MAPPER.getNodeFactory());
    }

    public static ArrayNode newArrayNode() {
        return new ArrayNode(MAPPER.getNodeFactory());
    }

    public static JsonNode toJsonNode(String source) {
        try {
            return MAPPER.readTree(source);
        } catch (Exception e) {
            throw new JsonDeserializationException(e);
        }
    }

    public static JsonNode toJsonNode(InputStream source) {
        try {
            return MAPPER.readTree(source);
        } catch (Exception e) {
            throw new JsonDeserializationException(e);
        }
    }

    public static JsonNode toJsonNode(byte[] source) {
        try {
            return MAPPER.readTree(source);
        } catch (Exception e) {
            throw new JsonDeserializationException(e);
        }
    }

    public static JsonNode toJsonNode(File source) {
        try {
            return MAPPER.readTree(source);
        } catch (Exception e) {
            throw new JsonDeserializationException(e);
        }
    }

    public static <T> T toObject(byte[] source, Class<T> tClass) {
        try {
            return MAPPER.readValue(source, tClass);
        } catch (Exception e) {
            throw new JsonDeserializationException(tClass, e);
        }
    }

    public static <T> T toObject(byte[] source, Type type) {
        try {
            return MAPPER.readValue(source, MAPPER.constructType(type));
        } catch (Exception e) {
            throw new JsonDeserializationException(type, e);
        }
    }

    public static <T> T toObject(byte[] source, TypeReference<T> typeReference) {
        try {
            return MAPPER.readValue(source, typeReference);
        } catch (Exception e) {
            throw new JsonDeserializationException(typeReference.getClass(), e);
        }
    }

    public static <T> T toObject(InputStream source, Class<T> tClass) {
        try {
            return MAPPER.readValue(source, tClass);
        } catch (Exception e) {
            throw new JsonDeserializationException(tClass, e);
        }
    }

    public static <T> T toObject(InputStream source, Type type) {
        try {
            return MAPPER.readValue(source, MAPPER.constructType(type));
        } catch (Exception e) {
            throw new JsonDeserializationException(type, e);
        }
    }

    public static <T> T toObject(InputStream source, TypeReference<T> typeReference) {
        try {
            return MAPPER.readValue(source, typeReference);
        } catch (Exception e) {
            throw new JsonDeserializationException(typeReference.getClass(), e);
        }
    }

    public static <T> T toObject(String source, Class<T> tClass) {
        try {
            return MAPPER.readValue(source, tClass);
        } catch (Exception e) {
            throw new JsonDeserializationException(tClass, e);
        }
    }

    public static <T> T toObject(String source, Type type) {
        try {
            return MAPPER.readValue(source, MAPPER.constructType(type));
        } catch (Exception e) {
            throw new JsonDeserializationException(type, e);
        }
    }

    public static <T> T toObject(String source, TypeReference<T> typeReference) {
        try {
            return MAPPER.readValue(source, typeReference);
        } catch (Exception e) {
            throw new JsonDeserializationException(typeReference.getClass(), e);
        }
    }

    public static Map<String, Object> toMapObject(String source) {
        try {
            return MAPPER.readValue(source, MAP_TYPE);
        } catch (Exception e) {
            throw new JsonDeserializationException(MAP_TYPE.getClass(), e);
        }
    }

    public static Map<String, String> toStringMapObject(String source) {
        try {
            return MAPPER.readValue(source, STRING_MAP_TYPE);
        } catch (Exception e) {
            throw new JsonDeserializationException(MAP_TYPE.getClass(), e);
        }
    }

    public static SortedMap<String, Object> toSortedMapObject(String source) {
        try {
            return MAPPER.readValue(source, SORTED_MAP_TYPE);
        } catch (Exception e) {
            throw new JsonDeserializationException(SORTED_MAP_TYPE.getClass(), e);
        }
    }

    public static SortedMap<String, String> toStringSortedMapObject(String source) {
        try {
            return MAPPER.readValue(source, STRING_SORTED_MAP_TYPE);
        } catch (Exception e) {
            throw new JsonDeserializationException(SORTED_MAP_TYPE.getClass(), e);
        }
    }

    public static List<Object> toListObject(String source) {
        try {
            return MAPPER.readValue(source, LIST_TYPE);
        } catch (Exception e) {
            throw new JsonDeserializationException(LIST_TYPE.getClass(), e);
        }
    }

    public static List<String> toStringListObject(String source) {
        try {
            return MAPPER.readValue(source, STRING_LIST_TYPE);
        } catch (Exception e) {
            throw new JsonDeserializationException(LIST_TYPE.getClass(), e);
        }
    }

    public static <T> List<T> toListObject(String source, Class<T> tClass) {
        try {
            return MAPPER.readValue(source, constructParametricType(List.class, tClass));
        } catch (Exception e) {
            throw new JsonDeserializationException(LIST_TYPE.getClass(), e);
        }
    }

    public static <T> List<T> toListObject(InputStream source, Class<T> tClass) {
        try {
            return MAPPER.readValue(source, constructParametricType(List.class, tClass));
        } catch (Exception e) {
            throw new JsonDeserializationException(LIST_TYPE.getClass(), e);
        }
    }

    public static <T> List<T> toListObject(File source, Class<T> tClass) {
        try {
            return MAPPER.readValue(source, constructParametricType(List.class, tClass));
        } catch (Exception e) {
            throw new JsonDeserializationException(LIST_TYPE.getClass(), e);
        }
    }

    public static JsonNode convertToJsonNode(Object from) {
        try {
            return MAPPER.valueToTree(from);
        } catch (Exception e) {
            throw new JsonSerializationException(e);
        }
    }

    public static <T> T convertTreeToObject(TreeNode from, Class<T> tClass) {
        try {
            return MAPPER.treeToValue(from, tClass);
        } catch (Exception e) {
            throw new JsonDeserializationException(tClass, e);
        }
    }

    public static <T> T convertTreeToObject(TreeNode from, Type type) {
        try {
            return MAPPER.treeToValue(from, MAPPER.constructType(type));
        } catch (Exception e) {
            throw new JsonDeserializationException(type, e);
        }
    }

    public static <T> T convertTreeToObject(TreeNode from, JavaType javaType) {
        try {
            return MAPPER.treeToValue(from, javaType);
        } catch (Exception e) {
            throw new JsonDeserializationException(javaType, e);
        }
    }

    public static <T> List<T> convertTreeToListObject(TreeNode from, Class<T> tClass) {
        try {
            return MAPPER.treeToValue(from, constructParametricType(List.class, tClass));
        } catch (Exception e) {
            throw new JsonDeserializationException(tClass, e);
        }
    }

    public static <T> T convertToObject(Object from, Class<T> tClass) {
        try {
            return MAPPER.convertValue(from, tClass);
        } catch (Exception e) {
            throw new JsonDeserializationException(tClass, e);
        }
    }

    public static <T> T convertToObject(Object from, Type type) {
        try {
            return MAPPER.convertValue(from, MAPPER.constructType(type));
        } catch (Exception e) {
            throw new JsonDeserializationException(type, e);
        }
    }

    public static <T> T convertToObject(Object from, TypeReference<T> typeReference) {
        try {
            return MAPPER.convertValue(from, typeReference);
        } catch (Exception e) {
            throw new JsonDeserializationException(typeReference.getClass(), e);
        }
    }

    public static Map<String, Object> convertToMapObject(Object from) {
        try {
            return MAPPER.convertValue(from, MAP_TYPE);
        } catch (Exception e) {
            throw new JsonDeserializationException(MAP_TYPE.getClass(), e);
        }
    }

    public static Map<String, String> convertToStringMapObject(Object from) {
        try {
            return MAPPER.convertValue(from, STRING_MAP_TYPE);
        } catch (Exception e) {
            throw new JsonDeserializationException(STRING_MAP_TYPE.getClass(), e);
        }
    }

    public static SortedMap<String, Object> convertToSortedMapObject(Object from) {
        try {
            return MAPPER.convertValue(from, SORTED_MAP_TYPE);
        } catch (Exception e) {
            throw new JsonDeserializationException(SORTED_MAP_TYPE.getClass(), e);
        }
    }

    public static SortedMap<String, String> convertToStringSortedMapObject(Object from) {
        try {
            return MAPPER.convertValue(from, STRING_SORTED_MAP_TYPE);
        } catch (Exception e) {
            throw new JsonDeserializationException(STRING_SORTED_MAP_TYPE.getClass(), e);
        }
    }

    public static List<Object> convertToListObject(Object from) {
        try {
            return MAPPER.convertValue(from, LIST_TYPE);
        } catch (Exception e) {
            throw new JsonDeserializationException(LIST_TYPE.getClass(), e);
        }
    }

    public static List<String> convertToStringListObject(Object from) {
        try {
            return MAPPER.convertValue(from, STRING_LIST_TYPE);
        } catch (Exception e) {
            throw new JsonDeserializationException(STRING_LIST_TYPE.getClass(), e);
        }
    }

    public static String toJsonString(Object from) {
        try {
            return MAPPER.writeValueAsString(from);
        } catch (JsonProcessingException e) {
            throw new JsonSerializationException(from.getClass(), e);
        }
    }

    public static byte[] toJsonBytes(Object from) {
        try {
            return MAPPER.writeValueAsBytes(from);
        } catch (JsonProcessingException e) {
            throw new JsonSerializationException(from.getClass(), e);
        }
    }

    private static class MapTypeReference extends TypeReference<Map<String, Object>> {
        // nothing
    }

    private static class StringMapTypeReference extends TypeReference<Map<String, String>> {
        // nothing
    }

    private static class SortedMapTypeReference extends TypeReference<TreeMap<String, Object>> {
        // nothing
    }

    private static class StringSortedMapTypeReference extends TypeReference<TreeMap<String, String>> {
        // nothing
    }

    private static class ListTypeReference extends TypeReference<List<Object>> {
        // nothing
    }

    private static class StringListTypeReference extends TypeReference<List<String>> {
        // nothing
    }

}
